// Copyright (c) Microsoft Corporation. 
// Licensed under the MIT license.

#pragma once

#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/UObjectGlobals.h"
#include "UObject/Object.h"

#include "SimplygonUE4UI.h"
#include "SimplygonPropertyCasters.h"

#include "SimplygonCustomPipelines.generated.h"

namespace
{

bool HasMinOneMaterialCaster(const TArray<USimplygonMaterialCaster*>& MaterialCasters)
{
	for (const auto* MaterialCaster : MaterialCasters)
	{
		if (MaterialCaster)
		{
			return true;
		}
	}
	return false;
}

}

UCLASS( DefaultToInstanced, BlueprintType, Blueprintable, abstract, editinlinenew )
class SIMPLYGONUOBJECTS_API USimplygonStandinPipeline : public UObject
{
	GENERATED_BODY()

public:
	/* Chart aggregation settings to adjust how UVs should be packed when flattening unreal engine materials.*/
	UPROPERTY(EditAnywhere, Category = "Flatten Material Settings")
	FChartAggregatorSettings ChartAggregatorSettings {};

	virtual bool HasValidSettings(FString& ErrorMsg) const { return true; }

#if ALLOW_SIMPLYGON
	virtual Simplygon::spPipeline CreatePipeline(Simplygon::ISimplygon* simplygon) { return Simplygon::NullPtr; }
#endif // ALLOW_SIMPLYGON

};

UCLASS(BlueprintType, Blueprintable, DisplayName = "Near")
class SIMPLYGONUOBJECTS_API UStandinNearPipeline : public USimplygonStandinPipeline
{
	GENERATED_BODY()
		
public:

	UPROPERTY(EditAnywhere, Category = "Settings")
	FAggregationPipelineSettings Settings;

	UStandinNearPipeline()
	{
		Settings.GeometryCullingSettings.UseClippingGeometry = true;
		Settings.GeometryCullingSettings.ClippingGeometrySelectionSetName = SG_CLIPPING_GEOMETRY_SELECTION_SET_NAME;
		Settings.MappingImageSettings.TexCoordGeneratorType = ETexCoordGeneratorType::ChartAggregator;
		Settings.VisibilitySettings.OccluderSelectionSetName = SG_OCCLUDER_GEOMETRY_SELECTION_SET_NAME;
	}

#if ALLOW_SIMPLYGON
	virtual Simplygon::spPipeline CreatePipeline(Simplygon::ISimplygon* simplygon) override
	{
		return Settings.CreatePipeline(simplygon);
	}
#endif // ALLOW_SIMPLYGON

};

UCLASS( BlueprintType, Blueprintable, DisplayName = "Far" )
class SIMPLYGONUOBJECTS_API UStandinFarPipeline : public USimplygonStandinPipeline
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, Category = "Settings")
	FRemeshingPipelineSettings Settings;

	UStandinFarPipeline()
	{
		Settings.GeometryCullingSettings.UseClippingGeometry = true;
		Settings.GeometryCullingSettings.ClippingGeometrySelectionSetName = SG_CLIPPING_GEOMETRY_SELECTION_SET_NAME;

		Settings.MaterialPropertyCasters.Add( CreateDefaultSubobject<UBaseColorCaster>(TEXT("DefaultBaseColor0")) );
		Settings.MaterialPropertyCasters.Add( CreateDefaultSubobject<UNormalCaster>(TEXT("DefaultNormal0")) );
	}

	bool HasValidSettings(FString& ErrorMsg) const override
	{
		if (!HasMinOneMaterialCaster(Settings.MaterialPropertyCasters))
		{
			ErrorMsg = "The Stand-in Far pipeline needs material property casters to function. Please add at least one.";
			return false;
		}
		return true;
	}

#if ALLOW_SIMPLYGON
	virtual Simplygon::spPipeline CreatePipeline(Simplygon::ISimplygon* simplygon) override
	{
		return Settings.CreatePipeline(simplygon);
	}
#endif // ALLOW_SIMPLYGON

};

UCLASS(DisplayName = "BillbordCloud")
class SIMPLYGONUOBJECTS_API UBillboardCloudPipeline : public USimplygonStandinPipeline
{
	GENERATED_BODY()

public:

	UPROPERTY(EditAnywhere, Category = "Settings")
	FBillboardCloudPipelineSettings Settings;

	UBillboardCloudPipeline()
	{
		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UBaseColorCaster>(TEXT("DefaultBaseColor0")));
		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UOpacityMaskCaster>(TEXT("DefaultOpacityMask0")));
		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UNormalCaster>(TEXT("DefaultNormal0")));

		//Map the correct opacity channel. We want to bake mask.
		UBaseColorCaster* BaseCaster = (UBaseColorCaster*)Settings.MaterialPropertyCasters[ 0 ];
		BaseCaster->ColorCasterSettings.OpacityChannel = TEXT( "OpacityMask" );

		UNormalCaster* NormalCaster = (UNormalCaster*)Settings.MaterialPropertyCasters[2];
		NormalCaster->NormalCasterSettings.Dilation = 0;
		NormalCaster->NormalCasterSettings.FillMode = EFillMode::NearestNeighbor;
	}

#if ALLOW_SIMPLYGON
	virtual Simplygon::spPipeline CreatePipeline(Simplygon::ISimplygon* simplygon) override
	{
		return Settings.CreatePipeline(simplygon);
	}
#endif


};

UCLASS()
class SIMPLYGONUOBJECTS_API UFlipbookPipeline : public USimplygonPipeline
{
	GENERATED_BODY()

public:

	UPROPERTY(EditAnywhere, Category = "Settings")
	FFlipbookPipelineSettings Settings;

	UFlipbookPipeline()
	{
		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UBaseColorCaster>(TEXT("DefaultBaseColor0")));
		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UOpacityMaskCaster>(TEXT("DefaultOpacityMask0")));
		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UNormalCaster>(TEXT("DefaultNormal0")));
		
		UBaseColorCaster* BaseCaster = (UBaseColorCaster*)Settings.MaterialPropertyCasters[ 0 ];
		BaseCaster->ColorCasterSettings.OpacityChannel = TEXT( "OpacityMask" );

		UNormalCaster* NormalCaster = (UNormalCaster*)Settings.MaterialPropertyCasters[2];
		NormalCaster->NormalCasterSettings.Dilation = 0;
		NormalCaster->NormalCasterSettings.FillMode = EFillMode::NearestNeighbor;
	}

#if ALLOW_SIMPLYGON
	virtual Simplygon::spPipeline CreatePipeline(Simplygon::ISimplygon* simplygon) override
	{
		return Settings.CreatePipeline(simplygon);
	}

#endif
};

UCLASS(DisplayName = "BillboardCloudFoliage")
class SIMPLYGONUOBJECTS_API UBillboardCloudVegetationPipeline : public USimplygonPipeline
{
	GENERATED_BODY()

public:

	UPROPERTY(EditAnywhere, Category = "Settings")
	FBillboardCloudVegetationPipelineSettings Settings;

	UBillboardCloudVegetationPipeline()
	{
		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UBaseColorCaster>(TEXT("DefaultBaseColor0")));
		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UOpacityMaskCaster>(TEXT("DefaultOpacityMask0")));
		UBaseColorCaster* BaseCaster = (UBaseColorCaster*)Settings.MaterialPropertyCasters[ 0 ];
		BaseCaster->ColorCasterSettings.OpacityChannel = TEXT( "OpacityMask" );
	}

#if ALLOW_SIMPLYGON
	virtual Simplygon::spPipeline CreatePipeline(Simplygon::ISimplygon* simplygon) override
	{
		return Settings.CreatePipeline(simplygon);
	}

#endif

};

UCLASS(BlueprintType, Blueprintable)
class SIMPLYGONUOBJECTS_API URemeshingPipeline : public USimplygonPipeline
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings")
	FRemeshingPipelineSettings Settings;

	URemeshingPipeline()
	{
		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UBaseColorCaster>(TEXT("DefaultBaseColor0")));
		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UNormalCaster>(TEXT("DefaultNormal0")));
	}

	bool HasValidSettings(FString& ErrorMsg) const override
	{
		if (!HasMinOneMaterialCaster(Settings.MaterialPropertyCasters))
		{
			ErrorMsg = "The remeshing pipeline needs material property casters to function. Please add at least one.";
			return false;
		}
		return true;
	}

#if ALLOW_SIMPLYGON
	virtual Simplygon::spPipeline CreatePipeline(Simplygon::ISimplygon* simplygon) override
	{
		return Settings.CreatePipeline(simplygon);
	}

#endif

private:
};

UCLASS(BlueprintType, Blueprintable)
class SIMPLYGONUOBJECTS_API UHighDensityMeshReductionPipeline : public USimplygonPipeline
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings")
	FHighDensityMeshReductionPipelineSettings Settings;

	UHighDensityMeshReductionPipeline()
	{
		Settings.HighDensityMeshReductionSettings.OnScreenSizeEnabled = true;
		Settings.HighDensityMeshReductionSettings.OnScreenSize = 5000;
	}

	bool HasValidSettings(FString& ErrorMsg) const override
	{
		if (!Settings.HighDensityMeshReductionSettings.OnScreenSizeEnabled && !Settings.HighDensityMeshReductionSettings.OnScreenSizeEnabled)
		{
			ErrorMsg = "The high density mesh reduction pipeline requires that at least one of the settings OnScreenSizeEnabled or MaxDeviationEnabled is set.";
			return false;
		}
		return true;
	}

#if ALLOW_SIMPLYGON
	virtual Simplygon::spPipeline CreatePipeline(Simplygon::ISimplygon* simplygon) override
	{
		return Settings.CreatePipeline(simplygon);
	}

#endif

private:
};


//NOTE : UCLASS is not allowed to be inside a pre processor block therefore this code is disabled.
// // Enable this if IMPOSTER_BAKER is defined
//UCLASS(DisplayName = "BillboardImposter")
//class SIMPLYGONUOBJECTS_API UImpostorFromSingleViewPipeline : public UObject
//{
//	GENERATED_BODY()
//
//public:
//
//	UPROPERTY(EditAnywhere, Category = "Settings")
//	FImpostorFromSingleViewPipelineSettings Settings;
//
//	UImpostorFromSingleViewPipeline()
//	{
//		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UBaseColorCaster>(TEXT("DefaultBaseColor0")));
//		Settings.MaterialPropertyCasters.Add(CreateDefaultSubobject<UOpacityMaskCaster>(TEXT("DefaultOpacityMask0")));
//	}
//
//#if ALLOW_SIMPLYGON
//	Simplygon::spPipeline CreatePipeline(Simplygon::ISimplygon* simplygon)
//	{
//		return Settings.CreatePipeline(simplygon);	
//	}
//
//#endif
//
//
//private:
//
//};
